<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8">
<meta name="Microsoft Theme" content="none">
<title>实验十  编写子程序</title>
</head>
<body background="../index.files/back_bluh.jpg">

<pre><font size="5" color="#3366FF"><b>实验十  编写子程序</b></font>


1，显示字符串</pre>
<pre>分析：</pre>
<pre>	在上一实验中我们就写过显示字符串的程序，这里我们只需要把原来自己设定的数据改成入口参数即可。</pre>
<pre>
【注：要仔细看它的提示，特别注意体会提示中后两点，在以后的编程中将非常重要】
	</pre>
<pre>实现：</pre>
<pre>	下面我们就直接给出这个子程序的源程序（包括检测部分）</pre>
<pre>
	程序见<a href="../pro/10_1.asm">10_1.asm</a>



2，解决除法溢出的问题</pre>
<pre>分析：
	在看给出的源程序之前，请自己仔细分析提示，写出源程序，那样你对这个问题的理解将更加清楚，在设计更加复杂的除法运算时将更加灵活，当然</pre>
<pre>设计更加复杂的除法运算还有更简单而且触及到二进制除法运算本质的算法。</pre>
<pre>
	下面我们分析，如何根据给出的公式设计程序：</pre>
<pre>
	1)公式：X/N=int(H/N)*65535+[rem(H/N)*65535+L]/N      
	
	X:被除数            N:除数，用cx保存
	H:被除数的高16位，用dx保存
	L:被除数的低16位，用ax保存
	
	2)下面是子程序的相关参数：</pre>
<pre>
	入口参数： dx,ax,cx     其中的内容见上面
	返回参数： dx,ax,cx     分别是结果的高16位置，低16位和余数
        
	3)现在我们结合公式进行相关的处理：</pre>
<pre>
	a，首先，我们根据给出的入口参数就可以求出int(H/N)和rem(H/N),只需要进行一次除法操作即可，但是问题马上就出来拉，用div进行除法操作时</pre>
<pre>的结果将保存到ax中，余数将保存到dx中，把dx和ax中原有的数据覆盖掉，从而影响后面要进行的相关操作，那么我们如何能够消除掉这种覆盖带来的影响</pre>
<pre>呢？把原有的数据进行保存。保存的办法很多，后面我们将就此进行分析。【之前需要保护ax，即被除数的低16位L】</pre>
<pre>
	b，之后我们可以先求出前一项，即int(H/N)*65536,而int(H/N)已经保存到ax中,所以直接用ax中数据进行乘法操作即可，进行完乘法操作后，原来</pre>
<pre>的dx，即上一步进行的除法操作的余数被覆盖掉，所以之前，我们也得进行保护工作。【之前需要保护dx,即rem(H/N)】</pre>
<pre>
	c，之后我们可以求后一项，[rem(H/N)*65536+L]/N,先求rem(H/N)*65536,之后求和，再求整个结果，由于这里的操作同样覆盖掉了上一步操作的结</pre>
<pre>果，所以操作前需要保存上一步的结果【之前需要上一步的dx和ax】</pre>
<pre>
	d，取出b中的结果，与c中的结果直接求和，把c步中dx中保存的值移到cx寄存器中，即可得到所要返回的参数</pre>
<pre>
	
4)关键问题分析：</pre>
<pre>
	通过上面的分析看到：实际上上面的操作是由除法，乘法，加法组成，涉及到三种指令:div,mul,add</pre>
<pre>
而最关键的不是这些简单的算术操作，而是如何把它们组装起来，如何用有限的寄存器处理比较大量的数据，如何处理子程序与外界的数据传递？

	a,如何保护运算过程中的相关数据？你也许可以想到寄存器，栈，或者内存，但是我们知道寄存器是有限的，栈操作受限制的线性表，而内存，</pre>
<pre>我们可以非常方便的对它进行访问，所以更方便简单的办法是用内存暂存的办法来保护相关数据。</pre>
<pre>
	b,如何处理子程序与外界的数据传递？</pre>
<pre>
	方法有：堆栈法，约定寄存器法，预定存储单元法，向量传递法</pre>
<pre>
	这里我们根据入口参数和出口参数知道，使用的是约定寄存器法。</pre>
<pre>

实现（包括检测部分）：</pre>
<pre>
	程序见<a href="../pro/10_2.asm">10_2.asm</a></pre>
<pre>
【注：这里存在两点：</pre>
<pre>
	1，在求int(H/N)和rem(H/N)的时候，必须对dx进行清除零操作，否则，进行的实际运算将不是我们要求的，而且可能存在溢出</pre>
<pre>
	2，后面在求[rem(H/N)*65536+L]/N时也进行了一个除法操作，这里不能对dx进行清零操作，不清零才是我们要求的，也不用担心此时会发生溢出，</pre>
<pre>因为我们用到的公式所实现的目的正是为了避免溢出而设计的</pre>
<pre>
	3，在设计的时候，这里考虑到了不会产生溢出的情况，也就是当(dx)&lt;(ax)时(请自己分析，为什么时这样？)，这样就可以避免不会发生溢出的输入</pre>
<pre>进行一些不必要的操作。】
</pre>
<pre>
3，数值显示</pre>
<pre>
	【注：这里直接考虑dword型的数据转变位表示十进制数的字符串，字符串以0结尾，主要是为了方便后面的课程设计一，当然它完全可以兼容书中要</pre>
<pre>求的（只需要使得dx位0即可）】</pre>
<pre>
分析：</pre>
<pre>	根据提示，我们需要做的有两点：</pre>
<pre>
	1)用除10取余操作得到各个位上数码位</pre>
<pre>
	2)把各个数码位加上30h即得到各个位对应的字符，按照与数码对应的顺序输出到屏幕上即可实现数值显示</pre>
<pre>
	当然这里存在两个问题：</pre>
<pre>
	a除10取余的时候，可能存在除法溢出？如果存在溢出，我们调用刚实现的子程序来解决</pre>
<pre>
	b如何控制除10循环的次数？根据书中的提示判断商是否位0，可以用jcxz实现</pre>
<pre>
二，实现：</pre>
<pre>
	先给出算法的大概流程：</pre>
<pre>
	1)为了可以使用第一个子程序，我们这里用ds：si指向转换后的字符的存放位置，假设从最后一位开始存放，所以先得初始化si为一个大于或者等于</pre>
<pre>数码位数的值。</pre>
<pre>
	2)取得入口参数：dx，ax  存放十进制数码的高16位和低16位</pre>
<pre>
	3)初始化被除数cx为10，调用divdw，取得该数码的最后一位（即divdw的返回参数cx），并加上30h得到它的ASCII码值，之后把cl保存到数据段中，</pre>
<pre>并且si减去一</pre>
<pre>
	4)判断dx和ax（divdw返回的参数）是否都为零，即判断所得的商是否为零，如果不为零，返回2；否则结束</pre>
<pre>
	5)si增加1</pre>
<pre>实现：</pre>
<pre>
	程序见：<a href="../pro/10_3.asm">10_3.asm	</a>

</pre>
<pre>
【子程序二的相关提示：关于二进制除法问题本质的揭露：
我们用小学就学过的除法计算方法：
	    00110010          
	    ――――
    	101|11111011
	    101
	    ――――
	     101
             101
	    ――――
	       0101
		101
	    ――――
		  01
根据计算不难看出，结果的商位00110010，余数为01，没有溢出。那么我们是否找到了它的本质呢？
其实，我们的计算过程就用到了计算的本质：从最高位开始，不小于除数的位置1，此时我们求除它们的差，并且替代被除数的对应位置；
小于除数的位置0，之后往前移一位，如此递归到最后一位，最后得到的差为余数。据此，我们可以得到任意位数的除法的算法。而且不可能存在溢出问题。
】</pre>
<div align="left">
  <PRE><center><b><a href="../"><font face="华文行楷" size="5" color="#3333FF">返回目录</font></a></b></center></pre>
</div>
